package me.stone.stanimeclient.core.ui.player

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.IdRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.media3.common.util.UnstableApi
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
import androidx.media3.ui.SubtitleView
import androidx.transition.TransitionManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import me.stone.stanimeclient.core.common.extensions.dip
import me.stone.stanimeclient.core.ui.R
import me.stone.stanimeclient.core.ui.databinding.AgeExoPlayerPanelBinding
import timber.log.Timber
import kotlin.math.max
import kotlin.math.roundToInt

@UnstableApi
class AgeExoPlayerUIBinding private constructor(
    private val agePlayerPanel: View,
    private val coroutineScope: CoroutineScope
) {

    companion object {
        @JvmStatic
        fun bind(panel: View, coroutineScope: CoroutineScope) =
            AgeExoPlayerUIBinding(panel, coroutineScope)

        @JvmStatic
        fun bind(binding: AgeExoPlayerPanelBinding, coroutineScope: CoroutineScope) =
            AgeExoPlayerUIBinding(binding.root, coroutineScope)

        private const val HIDE_DELAY_MILLIS = 1000L
    }

    // **********************************************************************
    // AgeExoPlayerPanel View Set

    private fun <T: View> panelViewBy(@IdRes id: Int) : T {
        return agePlayerPanel.findViewById(id)
    }

    private val topInfoLayout: ViewGroup = panelViewBy(R.id.top_info_layout)
    private val topInfoIcon: ImageView = panelViewBy(R.id.fast_speed_image)
    private val topInfoText: TextView = panelViewBy(R.id.top_info_text)

    private val infoLayout: ViewGroup = panelViewBy(R.id.info_layout)
    private val infoTitle: TextView = panelViewBy(R.id.info_text)
    private val infoSub: TextView = panelViewBy(R.id.info_subtext)

    private val volumeLayout: ViewGroup = panelViewBy(R.id.volume_gesture_layout)
    private val volumeProgressText: TextView = panelViewBy(R.id.volume_progress_text)
    private val volumeProgressBar: ProgressBar = panelViewBy(R.id.volume_progress_bar)

    private val brightnessLayout: ViewGroup = panelViewBy(R.id.brightness_gesture_layout)
    private val brightnessProgressText: TextView = panelViewBy(R.id.brightness_progress_text)
    private val brightnessProgressBar: ProgressBar = panelViewBy(R.id.brightness_progress_bar)

    private val horizontalSettingLayout: ViewGroup = panelViewBy(R.id.horizontal_volume_brightness)
    private val horizontalProgressText: TextView = panelViewBy(R.id.setting_progress_text)
    private val horizontalProgressBar: ProgressBar = panelViewBy(R.id.setting_progress_bar)
    private val horizontalSettingIcon: ImageView = panelViewBy(R.id.setting_image)

    val playerView: PlayerView = panelViewBy(R.id.player_view)


    // **********************************************************************
    // PlayerContent View Set

    private fun <T: View> playerViewBy(@IdRes id: Int) : T {
        return playerView.findViewById(id)
    }

    val aspectRatioFrameLayout: AspectRatioFrameLayout = playerViewBy(R.id.exo_content_frame)
    val shutterView: View = playerViewBy(R.id.exo_shutter)
    val bufferingView: ProgressBar = playerViewBy(R.id.exo_buffering)
    val subtitleView: SubtitleView = playerViewBy(R.id.exo_subtitles)
    val timeBar: AgePlayerTimeBar = playerView.findViewById(R.id.exo_progress)


    // **********************************************************************
    // Player Controllers Set

    private val textTitle: TextView = playerViewBy(R.id.title)
    private val btnBack: ImageButton = playerViewBy(R.id.btn_back)
    private val btnToTv: ImageButton = playerViewBy(R.id.btn_tv)
    private val btnMenu: ImageButton = playerViewBy(R.id.btn_menu)
    private val btnLock: ImageButton = playerViewBy(R.id.btn_lock)
    private val btnPlayOrPause: ImageButton = playerViewBy(R.id.btn_play_or_pause)
    private val btnFullscreen: ImageButton = playerViewBy(R.id.btn_fullscreen)
    private val btnEpisode: TextView = playerViewBy(R.id.btn_episodes)
    private val btnSpeed: TextView = playerViewBy(R.id.btn_speed)

    // 废弃
    // private val stateNetwork: TextView = playerViewBy(R.id.network_state)
    // private val stateBattery: ImageView = playerViewBy(R.id.battery_state)
    // private val groupHeadControllers: Group = playerViewBy(R.id.group_header_controller)

    private val groupHead: ConstraintLayout = playerViewBy(R.id.container_head)
    private val groupFoot: ConstraintLayout = playerViewBy(R.id.container_foot)

    // **********************************************************************
    // Player Controllers State Set

    private val _fullscreenFlow = MutableStateFlow(false)
    val fullscreenFlow: StateFlow<Boolean> = _fullscreenFlow
    val isFullscreen: Boolean
        get() = _fullscreenFlow.value

    private val _lockStateFlow = MutableStateFlow(false)
    val lockStateFlow: StateFlow<Boolean> = _lockStateFlow
    val isLockState: Boolean
        get() = _lockStateFlow.value

    // **********************************************************************
    // PlayerView Panel View Control Functions

    private var hideTopInfoJob: Job? = null

    fun showTopInfo(iconId: Int, info: String) {
        hideTopInfoJob?.cancel()
        if (topInfoLayout.visibility != View.VISIBLE) {
            topInfoLayout.visibility = View.VISIBLE
        }
        topInfoIcon.setImageResource(iconId)
        topInfoText.text = info
    }

    fun hideTopInfo(delayMillis: Long = HIDE_DELAY_MILLIS) {
        if (topInfoLayout.visibility != View.VISIBLE) return
        hideTopInfoJob = coroutineScope.launch {
            delay(delayMillis)
            topInfoLayout.visibility = View.GONE
        }
    }


    private var hideInfoJob: Job? = null

    fun showInfo(title: String, subTitle: String? = null) {
        hideInfoJob?.cancel()
        if (infoLayout.visibility != View.VISIBLE) {
            infoLayout.visibility = View.VISIBLE
        }
        infoTitle.text = title
        subTitle?.let {
            infoSub.text = it
            infoSub.visibility = View.VISIBLE
        } ?: let { infoSub.visibility = View.GONE }
    }

    fun hideInfo(delayMillis: Long = HIDE_DELAY_MILLIS) {
        if (infoLayout.visibility != View.VISIBLE) return
        hideInfoJob = coroutineScope.launch {
            delay(delayMillis)
            infoLayout.visibility = View.GONE
        }
    }

    private var hideVolumeJob: Job? = null

    fun showVolumeInfo(volumeManager: VolumeManager) {
        val progress = volumeManager.currentVolume.times(100).toInt()
        val maxValue = volumeManager.maxVolume.times(100)
        val percentage = volumeManager.volumePercentage

        if (!isFullscreen) {
            hideVolumeInfo(0)
            showSettingInfo(progress, maxValue, R.drawable.ic_common_volume_2)
            return
        }

        hideBrightnessInfo(0)
        hideVolumeJob?.cancel()
        if (volumeLayout.visibility != View.VISIBLE) {
            volumeLayout.visibility = View.VISIBLE
        }
        volumeProgressBar.max = maxValue
        volumeProgressBar.progress = progress
        volumeProgressText.text = percentage.toString()
    }

    fun hideVolumeInfo(delayMillis: Long = HIDE_DELAY_MILLIS) {
        hideSettingInfo(delayMillis)
        if (volumeLayout.visibility != View.VISIBLE) return
        hideVolumeJob = coroutineScope.launch {
            delay(delayMillis)
            volumeLayout.visibility = View.GONE
        }
    }

    private var hideBrightnessJob: Job? = null

    fun showBrightnessInfo(brightnessManager: BrightnessManager) {
        val progress = brightnessManager.currentBrightness.times(100).toInt()
        val maxValue = brightnessManager.maxBrightness.times(100).toInt()
        val percentage = brightnessManager.brightnessPercentage

        if (!isFullscreen) {
            hideBrightnessInfo(0)
            showSettingInfo(progress, maxValue, R.drawable.ic_common_sun)
            return
        }

        hideVolumeInfo(0)
        hideBrightnessJob?.cancel()
        if (brightnessLayout.visibility != View.VISIBLE) {
            brightnessLayout.visibility = View.VISIBLE
        }
        brightnessProgressBar.max = maxValue
        brightnessProgressBar.progress = progress
        brightnessProgressText.text = percentage.toString()
    }

    fun hideBrightnessInfo(delayMillis: Long = HIDE_DELAY_MILLIS) {
        hideSettingInfo(delayMillis)
        if (brightnessLayout.visibility != View.VISIBLE) return
        hideBrightnessJob = coroutineScope.launch {
            delay(delayMillis)
            brightnessLayout.visibility = View.GONE
        }
    }

    private var hideSettingLayoutJob: Job? = null

    fun showSettingInfo(progress: Int, max: Int, iconId: Int) {
        hideSettingLayoutJob?.cancel()
        if (horizontalSettingLayout.visibility != View.VISIBLE) {
            horizontalSettingLayout.visibility = View.VISIBLE
        }
        horizontalProgressBar.max = max
        horizontalProgressBar.progress = progress
        horizontalProgressText.text = (progress.toFloat() / max).times(100).toInt().toString()
        horizontalSettingIcon.setImageResource(iconId)
    }

    fun hideSettingInfo(delayMillis: Long = HIDE_DELAY_MILLIS) {
        if (horizontalSettingLayout.visibility != View.VISIBLE) return
        hideSettingLayoutJob = coroutineScope.launch {
            delay(delayMillis)
            horizontalSettingLayout.visibility = View.GONE
        }
    }

    // **********************************************************************
    // PlayerView Controller View Control Functions

    enum class Action(@IdRes val resId: Int) {
        Back(R.id.btn_back),
        ToTv(R.id.btn_tv),
        Menu(R.id.btn_menu),
        Lock(R.id.btn_lock),
        PlayOrPause(R.id.btn_play_or_pause),
        Fullscreen(R.id.btn_fullscreen),
        Episode(R.id.btn_episodes),
        Speed(R.id.btn_speed),
    }

    private val _actionFlow = MutableSharedFlow<Action>()
    val actionFlow: SharedFlow<Action> = _actionFlow

    private val actionClickListener = View.OnClickListener { v ->
        Timber.d("on view click -> $v")
        Action.entries.find { it.resId == v.id }?.let { action ->
            Timber.d("find action: $action")
            coroutineScope.launch { _actionFlow.emit(action) }
        }
    }

    private val actionButtons: Array<View>
        get() = arrayOf(
            btnBack, btnToTv, btnMenu, btnLock,
            btnPlayOrPause, btnFullscreen,
            btnEpisode, btnSpeed,
        )

    init {
        actionButtons.forEach {
            it.setOnClickListener(actionClickListener)
        }
    }

    private val defaultHeadSet = ConstraintSet()
    private val defaultFootSet = ConstraintSet()
    private val defaultPanelSet = ConstraintSet()

    private val fullscreenHeadSet = ConstraintSet()
    private val fullscreenFootSet = ConstraintSet()
    private val fullscreenPanelSet = ConstraintSet()

    private val controllerView: ConstraintLayout
        get() = btnLock.parent as ConstraintLayout

    init {
        Timber.d("controller view: $controllerView")
        defaultPanelSet.clone(controllerView)
        defaultFootSet.clone(groupFoot)
        defaultHeadSet.clone(groupHead)

        LayoutInflater.from(playerView.context).inflate(
            R.layout.age_exo_player_controller_fullscreen, playerView, false
        ).let { it as ConstraintLayout }.run {
            fullscreenPanelSet.clone(this)
            findViewById<ConstraintLayout>(R.id.container_head).let {
                Timber.d("head padding top: ${it.paddingTop}")
                fullscreenHeadSet.clone(it)
            }
            findViewById<ConstraintLayout>(R.id.container_foot).let {
                Timber.d("foot padding bot: ${it.paddingBottom}")
                fullscreenFootSet.clone(it)
            }
        }
    }

    fun showFullscreenUI() {
        if (isFullscreen) return
        coroutineScope.launch { _fullscreenFlow.emit(true) }

        TransitionManager.beginDelayedTransition(controllerView)
        controllerView.postOnAnimation {
            fullscreenHeadSet.applyTo(groupHead)
            fullscreenFootSet.applyTo(groupFoot)
            fullscreenPanelSet.applyTo(controllerView)

            controllerView.postOnAnimation {
                val dp8 = 8f.dip.roundToInt()
                groupHead.setPadding(dp8, dp8 * 3, dp8, dp8)
                groupFoot.setPadding(dp8, dp8, dp8, dp8)
            }
        }
    }

    fun backFullscreenUI() {
        if (!isFullscreen) return
        coroutineScope.launch { _fullscreenFlow.emit(false) }

        TransitionManager.beginDelayedTransition(controllerView)
        controllerView.postOnAnimation {
            defaultHeadSet.applyTo(groupHead)
            defaultFootSet.applyTo(groupFoot)
            defaultPanelSet.applyTo(controllerView)

            controllerView.postOnAnimation {
                val dp8 = 8f.dip.roundToInt()
                groupHead.setPadding(0, 0, 0, dp8)
                groupFoot.setPadding(0, dp8, 0, 0)
            }
        }
    }

    fun lockControllers() {
        if (isLockState) return
        btnLock.setImageResource(R.drawable.ic_player_lock)
        groupFoot.visibility = View.GONE
        groupHead.visibility = View.GONE
        coroutineScope.launch { _lockStateFlow.emit(true) }
    }

    fun unlockControllers() {
        if (!isLockState) return
        btnLock.setImageResource(R.drawable.ic_player_unlock)
        groupFoot.visibility = View.VISIBLE
        groupHead.visibility = View.VISIBLE
        coroutineScope.launch { _lockStateFlow.emit(false) }
    }

    fun updateTitle(title: String) {
        textTitle.text = title
    }

//    fun updateNetState(mode: NetworkMode) {
//        stateNetwork.text = mode.modeName
//    }

//    fun updateBatteryState(mode: BatteryState) {
//        when (mode) {
//            BatteryState.Warning -> R.drawable.ic_common_battery_warning
//            BatteryState.Low -> R.drawable.ic_common_battery_low
//            BatteryState.Medium -> R.drawable.ic_common_battery_medium
//            BatteryState.Full -> R.drawable.ic_common_battery_large
//            BatteryState.Charging -> R.drawable.ic_common_battery_charge
//        }.let { stateBattery.setImageResource(it) }
//    }

    fun updatePlayOrPauseButtonIcon(isPlaying: Boolean) {
        btnPlayOrPause.setImageResource(
            if (isPlaying) R.drawable.ic_player_pause
            else R.drawable.ic_player_play
        )
    }

}
